/*
 * Copyright (C) 2013, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include "vfsio.h"
#include "vfscore.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int vfs_fclose(VFILE * stream)
{
	int result = EOF;
	if (stream)
	{
		if (stream->m_mod && stream->m_mod->f_fclose)
		{
			result = stream->m_mod->f_fclose(stream->m_mod_data);
		}
		if (stream->m_name)
		{
			free(stream->m_name);
		}
		free(stream);
	}
	return result;
}

int vfs_fflush(VFILE * stream)
{
	if (stream && stream->m_mod && stream->m_mod->f_fflush)
	{
		return stream->m_mod->f_fflush(stream->m_mod_data);
	}
	return EOF;
}

VFILE * vfs_fopen(const char * name, const char * mode)
{
	size_t i;
	VFILE * result = (VFILE *)malloc(sizeof(VFILE));
	if (result)
	{
		memset(result, 0, sizeof(VFILE));
		// check in real fs
		for (i = 0; i < REAL_MOD_COUNT; ++i)
		{
			result->m_mod = &g_real_mods[i];
			if (result->m_mod && result->m_mod->f_fopen)
			{
				result->m_mod_data = result->m_mod->f_fopen(name, mode, result->m_mod);
			}
			if (result->m_mod_data)
			{
				break;
			}
		}
		// check in virt fs
		if (!result->m_mod_data)
		{
			for (i = 0; i < g_virt_mods_count; ++i)
			{
				result->m_mod = &g_virt_mods[i];
				if (result->m_mod && result->m_mod->f_fopen)
				{
					result->m_mod_data = result->m_mod->f_fopen(name, mode, result->m_mod);
				}
				if (result->m_mod_data)
				{
					break;
				}
			}
		}
		if (result->m_mod_data && name)
		{
			result->m_name = (char *)malloc(strlen(name) + 1);
			if (result->m_name)
			{
				strcpy(result->m_name, name);
			}
		}
		if (!result->m_name)
		{
			if (result->m_mod && result->m_mod_data)
			{
				result->m_mod->f_fclose(result->m_mod_data);
			}
			free(result);
			result = NULL;
		}
	}
	return result;
}

size_t vfs_fread(void * dst, size_t size, size_t num, VFILE * stream)
{
	if (stream && stream->m_mod && stream->m_mod->f_fread)
	{
		return stream->m_mod->f_fread(dst, size, num, stream->m_mod_data);
	}
	return 0;
}

int vfs_fseek(VFILE * stream, long int offset, int origin)
{
	if (stream && stream->m_mod && stream->m_mod->f_fseek)
	{
		return stream->m_mod->f_fseek(stream->m_mod_data, offset, origin);
	}
	return -1;
}

long int vfs_ftell(VFILE * stream)
{
	if (stream && stream->m_mod && stream->m_mod->f_ftell)
	{
		return stream->m_mod->f_ftell(stream->m_mod_data);
	}
	return -1;
}

size_t vfs_fwrite(const void * src, size_t size, size_t num, VFILE * stream)
{
	if (stream && stream->m_mod && stream->m_mod->f_fwrite)
	{
		return stream->m_mod->f_fwrite(src, size, num, stream->m_mod_data);
	}
	return 0;
}

